package com.junglone.fang.websocket;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * Description:
 *
 * @author junglone: <a href="mailto:cjlnzb@hotmail.com">cjlnzb@hotmail.com</a>
 * @version 1.0.0
 * @since 2019/08/15 10:27
 */
@ServerEndpoint("/websocket/{roomNum}/{sid}/{status}")
@Component
public class WebSocketServer {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);

    /**
     * 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
     */
    private static int onlineCount = 0;
    /**
     * concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
     */
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 接收sid
     */
    private String sid = "";


    /**
     * roomNum
     */
    private String roomNum = "";


    /**
     * 设置正在匹配的 sid
     */
    private static HashSet<String> hsPairing = new HashSet<>();

    /**
     * 设置匹配成功的两个用户
     */
    private static HashMap<String, String> hmPaired = new HashMap<>();
    /**
     * 设置匹配成功的两个用户
     */
    private static HashMap<String, String> hmPairedRoom = new HashMap<>();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("roomNum") String roomNum, @PathParam("sid") String sid, @PathParam("status") String strStatus) {
        this.session = session;
        this.sid = sid;
        this.roomNum = roomNum;

        //加入set中
        webSocketSet.add(this);

        //在线数加1
        addOnlineCount();

        switch (strStatus) {

            case "online":

                LOGGER.info("有新窗口开始监听: {}, 当前在线人数为: {}", sid, getOnlineCount());
                try {
                    sendMessage("连接成功, 当前在线人数为: " + getOnlineCount());
                } catch (IOException e) {
                    e.printStackTrace();
                }

                break;

            case "Pairing":

                LOGGER.info("Pairing ----- sid = {}", sid);

                // 找到对手
                String strPairedId = hmPaired.get(sid);
                LOGGER.info("Pairing ----- strPairedId = {}", strPairedId);
                if (null != strPairedId) {
                    try {
                        sendMessage("恭喜 " + sid + " 匹配成功, 你的对手是: " + strPairedId);
                        subOnlineCount();
                        // 清除房间及匹配信息
                        hmPaired.remove(sid);
                        hmPaired.remove(strPairedId);
                        hmPairedRoom.remove(sid);
                        hmPairedRoom.remove(strPairedId);
                    } catch (IOException e) {
                        LOGGER.error("websocket IO异常");
                    }
                }
                break;
            default:
                break;
        }

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        // 从set中删除
        webSocketSet.remove(this);
        // 在线数减1
        subOnlineCount();
        LOGGER.info("有一连接关闭！当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message) {
        LOGGER.info("收到来自窗口 roomNum = " + roomNum + " 的信息: " + message);
        // 群发消息
        for (WebSocketServer item : webSocketSet) {
            try {

                if (item.roomNum.equals(roomNum)) {
                    item.sendMessage(message);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        LOGGER.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
        LOGGER.info("推送消息到窗口" + sid + "，推送内容:" + message);
        for (WebSocketServer item : webSocketSet) {
            try {
                //这里可以设定只推送给这个sid的，为null则全部推送
                if (sid == null) {
                    item.sendMessage(message);
                } else if (item.sid.equals(sid)) {
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

    /**
     * Description: 取得配对成功的 id
     *
     * @param strId strID 当前请求配对的 id
     *
     * @return java.lang.String 匹配成功的 id, 匹配失败返回 null;
     *
     * @author junglone: <a href="mailto:cjlnzb@hotmail.com">cjlnzb@hotmail.com</a>
     * @since 2019/08/15 16:11
     */
    public static synchronized String getPaired(String strId) {
        String strRoomNum = hmPairedRoom.get(strId);

        if (null != strRoomNum) {
            return strRoomNum;
        }

        if (!hsPairing.iterator().hasNext()) {
            return null;
        }

        String strPairedId = hsPairing.iterator().next();
        LOGGER.info("start --------- 匹配成功的 id: {}", strPairedId);

        // 自己不能匹配自己
        if (strId.equals(strPairedId)) {
            // 删除自己
            hsPairing.remove(strId);
            if (hsPairing.iterator().hasNext()) {
                strPairedId = hsPairing.iterator().next();
            } else {
                // 如果没有更多的人正在匹配中, 再把自己加进来
                hsPairing.add(strId);
                return null;
            }
        }

        LOGGER.info("final --------- 匹配成功的 id: {}", strPairedId);

        // 生成一个随机的房间号
        long lRoomNum = Math.round(Math.random() * 1000000);
        strRoomNum = String.valueOf(lRoomNum);
        // 把自己/对手作为 key, 房间号作为 value 放入匹配 map
        hmPairedRoom.put(strId, strRoomNum);
        hmPairedRoom.put(strPairedId, strRoomNum);

        hmPaired.put(strId, strPairedId);
        hmPaired.put(strPairedId, strId);

        // 删除在排队的对手
        hsPairing.remove(strPairedId);

        return strRoomNum;

    }


    public static synchronized void joinPairing(String strId) {
        // 把自己加入正在匹配列表
        hsPairing.add(strId);
    }
}